package com.sondertara.joya.core.jdbc.dao.support;

import com.sondertara.common.collection.BiMap;
import com.sondertara.joya.core.cache.CacheHolder;
import com.sondertara.joya.core.jdbc.statment.creator.SelectSqlCreator;
import com.sondertara.joya.core.jdbc.support.TableMetaContext;
import com.sondertara.joya.core.model.EntityTableMetaDefinition;
import com.sondertara.joya.core.query.LinkType;
import com.sondertara.joya.core.query.criterion.WhereCondition;
import com.sondertara.joya.exceptions.IllegalSqlException;
import lombok.Data;

import javax.persistence.Table;
import java.beans.Introspector;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Data
@Table(name = "test_class")
public class TestEntity implements Serializable {
    private String b4y;
    private String id1;
    private String subSystem;
    private String colOrBuy;

    public static void main(String[] args) {
        String string = "B4yStartsWithAndId1AndColOrBuyOrSubSystem";


        String[] ors = string.split("Or");

        List<OrPart> list = Arrays.stream(ors).map(source -> {

            OrPart part = new OrPart(source, TestEntity.class);
            return part;
        }).collect(Collectors.toList());

        StringJoiner sj = new StringJoiner(" ");
        StringBuilder candidate = new StringBuilder();
        AtomicInteger loop = new AtomicInteger(0);

        List<PredicatePart> parts = new ArrayList<>();


        for (int j = 0; j < list.size(); j++) {
            OrPart part = list.get(j);
            boolean needVisitNext = false;
            System.out.println(part.getSource());
            for (int i = 0; i < part.getChildren().size(); i++) {
                loop.set(i);
                PredicatePart predicatePart = findPart(loop, candidate, part.getChildren());
                //find
                if (null != predicatePart) {
                    //is Last one append or
                    if (loop.get() == part.getChildren().size() - 1) {
                        predicatePart.setOrPartEnd(true);

                    }
                    parts.add(predicatePart);
                    candidate.delete(0, candidate.length());
                    if (loop.get() > i) {
                        System.out.println("gggg");
                        i = loop.get();
                    }
                } else {
                    //没找到说明Or将正常字段分隔，找下一个
                    needVisitNext = true;
                    candidate.append("Or");
                    break;
                }
            }

            if (needVisitNext) {
                continue;
            }

            if (j != list.size() - 1) {
                sj.add("Or");
            }
        }

        if (candidate.length()>0){
            throw new IllegalSqlException("No property name found by name({}) for the method({}),please check it",candidate,"fff");
        }

        System.out.println(sj);

        StringJoiner stringJoiner = new StringJoiner(" ");
        WhereCondition<TestEntity> original = new WhereCondition<>(LinkType.OR);


        List<PredicatePart> andPart = new ArrayList<>();

        for (int i = 0; i < parts.size(); i++) {
            PredicatePart part = parts.get(i);
            String columnName = part.getPropertyPath().getName();
            stringJoiner.add(columnName);
            if (i < parts.size() - 1) {
                stringJoiner.add(part.isOrPartEnd() ? "OR" : "AND");
            }

            andPart.add(part);
            //or子查询结束
            if (part.isOrPartEnd()) {
                original.and(condition -> {
                    for (PredicatePart predicatePart : andPart) {
                        MethodPredicateType methodPredicateType = predicatePart.getMethodPredicateType();
                        String name = predicatePart.getPropertyPath().getName();
                        switch (methodPredicateType) {
                            case STARTING_WITH:
                                condition.startsWith(name, "start");
                                break;
                            default:
                                condition.eq(name, "joya");
                        }
                    }
                    return condition;
                });
                andPart.clear();
            }


        }
        System.out.println(stringJoiner);
        TableMetaContext context = TableMetaContext.of(TestEntity.class);
        SelectSqlCreator<TestEntity> creator = new SelectSqlCreator<>(context);
        creator.setWhereCriterion(original);
        System.out.println(creator.create());
        System.out.println(Arrays.toString(creator.createParameters()));


    }

    private static PredicatePart findPart(AtomicInteger loop, StringBuilder prefix, List<PredicatePart> list) {
        PredicatePart predicatePart = list.get(loop.get());

        PropertyPath propertyPath = predicatePart.getPropertyPath();

        String decapitalize = Introspector.decapitalize(prefix + propertyPath.getName());

        if (prefix.length() == 0) {
            prefix.append(decapitalize);
        } else {
            prefix.append(propertyPath.getName());
        }
        EntityTableMetaDefinition metaDefinition = CacheHolder.tableMetaCache().get(TestEntity.class).get();
        Map<String, String> map = metaDefinition.columnPropertyRelationBiMap().inverse();
        // find field
        String propertyName = map.get(decapitalize);
        if (null != propertyName) {
            return buildPredicatePart(predicatePart, propertyName);
        }

        // not found,then append type string
        if (null != predicatePart.getSourceSuffix()) {
            propertyName = map.get(prefix + predicatePart.getSourceSuffix());
            if (null != propertyName) {
                return buildPredicatePart(predicatePart, propertyName);
            }
        }
        //找下一个
        if (loop.incrementAndGet() < list.size()) {
            prefix.append("And");
            return findPart(loop, prefix, list);
        }
        return null;
    }

    private static PredicatePart buildPredicatePart(PredicatePart original, String propertyName) {

        PropertyPath path = new PropertyPath(propertyName, original.getPropertyPath().getOwner());
        return new PredicatePart(path, original.getMethodPredicateType());
    }

    @Data
    private static class PredicateType {

        private PredicateType next;

        private String type;

        private String path;

        private PredicateType child;

    }
}
